﻿using System;
using System.Linq.Expressions;
using System.Text;
using FluentDml.Linq;
using NHibernate;

namespace FluentDml
{
    /// <summary>
    /// Apply DML based commands using Linq and NHibernate
    /// </summary>
    /// <typeparam name="T">the type to apply set based operations against</typeparam>
    public abstract class Command<T>
    {
        private readonly ISession _session;
        protected readonly Expression<Func<T, bool>> Where;
        protected ParameterAggregator ParameterAggregator = new ParameterAggregator();

        protected Command(ISession session, Expression<Func<T, bool>> where)
        {
            _session = session;
            Where = where;
        }

        /// <summary>
        /// generates a HQL query for the set based command
        /// </summary>
        /// <returns></returns>
        public virtual string GenerateCommand()
        {
            var hqlCommand = HqlWhereClauseGenerator.Generate(ParameterAggregator, Where);

            StringBuilder buffer = new StringBuilder();
            string alias = hqlCommand.IsSubQuery ? "t" : "x";
            
            buffer.Append(CreateCommandString(alias));

            buffer.Append("where ");

            if (hqlCommand.IsSubQuery)
            {
                buffer.AppendFormat("t in (from {0} as x where {1})", typeof(T).FullName, hqlCommand.Hql);
            }
            else
            {
                buffer.Append(hqlCommand.Hql);
            }

            return buffer.ToString();
        }

        /// <summary>
        /// executes the DML command against the session 
        /// </summary>
        public virtual int ExecuteCommand()
        {
            if (_session == null)
                throw new NullReferenceException("should create this using the ctor(ISession, Expression)");

            IQuery query = _session.CreateQuery(GenerateCommand());

            foreach (var parameter in ParameterAggregator.GetParameters())
            {
                query.SetParameter(parameter.Name, parameter.Value);
            }

            return query.ExecuteUpdate();
        }

        /// <summary>
        /// creates the command string, not including the where clause
        /// </summary>
        protected abstract string CreateCommandString(string alias);
       
    }
}